import math, typing

from strange_io import SIGNATURE, get_format, _load_bytearray
import strange_io.dynamic_integer as dynamic_integer

class StrangeFloat:
    def __init__(self, number=0.0, endian:typing.Literal['little','big']='big'):
        self.number = number
        self.endian = endian

    def dump(self) -> float:
        """ Get decoded number """
        return self.number

    def load(self, number: float):
        """ Encode and store number """
        self.number = number

    def dump_bytes(self) -> bytes:
        """ Get encoded number """
        return self.encode(self.number, self.endian)

    def load_bytes(self, number: bytes):
        """ Store encoded number """
        out = self.decode(number)
        self.number = out[0]
        self.endian = out[1]

    def dump_file(self, path):
        """ Dump number to file """
        out_file = bytearray()
        header = bytearray(SIGNATURE + b'float\n')
        out_file += header
        out_file += self.encode(self.number, self.endian)
        with open(path, 'wb') as f:
            f.write(out_file)
            f.close()

    def load_file(self, path, ignore_type=False):
        """ Load number from file """
        if get_format(path) == b'float' or ignore_type:
            file = _load_bytearray(path, remove_type=True)
            out = self.decode(file)
            self.number = out[0]
            self.endian = out[1]
        else:
            raise TypeError('Attempted to load non-integer document as integer document')

    @staticmethod
    def encode(number: float, endian:typing.Literal['little','big']) -> bytes:
        out = bytearray()
        if number < 0 and endian == 'big':
            out += bytearray([1])
        elif number < 0 and endian == 'little':
            out += bytearray([2])
        elif number >= 0 and endian == 'big':
            out += bytearray([3])
        elif number >= 0 and endian == 'little':
            out += bytearray([4])
        else:
            raise ValueError()
        halfs = (int(str(abs(number)).split('.')[0]), int(str(abs(number)).split('.')[1]))
        out += dynamic_integer.encode(halfs[0], endian)[0]
        out += dynamic_integer.encode(halfs[1], endian)[0]
        return bytes(out)

    @staticmethod
    def decode(number: bytes) -> tuple[float, typing.Literal['little','big']]:
        number = bytearray(number)
        head = number[0]
        del number[0]
        if head == 1:
            negative = True
            endian = 'big'
        elif head == 2:
            negative = True
            endian = 'little'
        elif head == 3:
            negative = False
            endian = 'big'
        elif head == 4:
            negative = False
            endian = 'little'
        else:
            raise LookupError('invalid header value')
        halfs = [0,0]
        halfs[0] = dynamic_integer.decode(number, endian)[0]
        halfs[1] = dynamic_integer.decode(number, endian)[0]
        out_number = float(f'{halfs[0]}.{halfs[1]}')
        if negative:
            out_number = -abs(out_number)
        return out_number, endian

    def __str__(self) -> str:
        return f'<StrangeFloat \'{self.endian}\' \'{self.number}\'>'
